home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac Power 1997 December
/
MACPOWER-1997-12.ISO.7z
/
MACPOWER-1997-12.ISO
/
AMUG
/
PROGRAMMING
/
Raven 1.2.sit
/
Raven 1.2
/
Source
/
Foundation
/
Common
/
ZMiscUtils.cpp
< prev
next >
Wrap
Text File
|
1997-08-16
|
17KB
|
635 lines
/*
* File: ZMiscUtils.cpp
* Summary: Misc utilities
* Written by: Jesse Jones
*
* Copyright ゥ 1996-1997 Jesse Jones.
* For conditions of distribution and use, see copyright notice in ZTypes.h
*
* Change History (most recent first):
*
* <8> 8/16/97 JDJ ReportError now takes an OSStatus.
* <7> 8/06/97 JDJ ReportError uses StandardAlert.
* <6> 7/27/97 JDJ Added ByteSwap(double).
* <5> 4/13/97 JDJ TCodeTimer and TAverageTimer descend from MExitAction.
* <4> 4/05/97 JDJ Added a pragma unused.
* <3> 1/09/97 JDJ ReportError uses the Notification Manager if the
* app is in the background.
* <2> 12/05/96 JDJ Added TAverageTimer.
* <1> 1/13/96 JDJ Created
*/
#include <ZMiscUtils.h>
#include <Aliases.h>
#include <Appearance.h>
#include <Dialogs.h>
#include <Errors.h>
#include <Limits.h>
#include <Memory.h>
#include <Processes.h>
#include <Resources.h>
#include <StdIo.h>
#include <String>
#include <Timer.h>
#include <ToolUtils.h>
#include <ZConstants.h>
#include <ZDebug.h>
#include <ZExceptions.h>
#include <ZGestalt.h>
#include <ZNotify.h>
#include <ZStringUtils.h>
#include <ZTypes.h>
// ========================================================================================
// Internal Functions
// ========================================================================================
//---------------------------------------------------------------
//
// IsInForeground
//
//---------------------------------------------------------------
static bool IsInForeground()
{
Boolean inFront = true;
ProcessSerialNumber myProcess;
myProcess.highLongOfPSN = 0;
myProcess.lowLongOfPSN = kCurrentProcess;
ProcessSerialNumber frontProcess;
OSErr err = GetFrontProcess(&frontProcess);
if (err == noErr)
err = SameProcess(&myProcess, &frontProcess, &inFront);
ASSERT(err == noErr);
return inFront;
}
//---------------------------------------------------------------
//
// DoStop
//
//---------------------------------------------------------------
static short DoStop(ConstStr255Param str) // takes a Str255 to avoid ambiguities with DoStop in ZDialogUtils.h
{
InitCursor();
ParamText(str, "¥p", "¥p", "¥p");
short item = StopAlert(130, nil);
return item;
}
#pragma mark -
// ========================================================================================
// Errors
// ========================================================================================
//---------------------------------------------------------------
//
// ReportError (string, string, bool)
//
//---------------------------------------------------------------
void ReportError(const string& errorStr, const string& supplementalStr, bool notify)
{
#pragma unused(notify)
#if !DRAG_AND_DROP_APP // TNotify and StateBroadcaster are overkill for a simple d&d app
if (notify || !IsInForeground()) { // ・・・ハThis seems like a pretty crude way to handle this...
TNotify* note = new TNotify(errorStr + supplementalStr);
note->Post();
} else
#endif
{
if (UGestalt::hasAppearanceMgr) {
AlertStdAlertParamRec params;
params.movable = false;
params.helpButton = false;
params.filterProc = nil;
params.defaultText = (StringPtr) -1L; // use default (ie "OK")
params.cancelText = nil;
params.otherText = nil;
params.defaultButton = ok;
params.cancelButton = 0;
params.position = kWindowDefaultPosition;
short item;
OSErr err = StandardAlert(kAlertStopAlert, StrToPStr(errorStr), StrToPStr(supplementalStr), ¶ms, &item);
ASSERT(err == noErr); // don't throw
} else
DoStop(StrToPStr(errorStr + supplementalStr));
}
}
//---------------------------------------------------------------
//
// ReportError (string, OSStatus, bool)
//
//---------------------------------------------------------------
void ReportError(const string& errorStr, OSStatus err, bool notify)
{
ASSERT(err != noErr);
Str255 numberStr;
NumToString(err, numberStr);
string supplementalStr = LoadRavenString(" because of an error #") + PStrToStr(numberStr);
string text = LookUpString(203, err);
if (text != "")
supplementalStr += LoadRavenString(" (") + text + LoadRavenString(").");
else
supplementalStr += LoadRavenString(". (period)");
ReportError(errorStr, supplementalStr, notify);
}
//---------------------------------------------------------------
//
// ReportError (string, TBaseException, bool)
//
//---------------------------------------------------------------
void ReportError(const string& errorStr, const TBaseException& e, bool notify)
{
if (const TSystemException* sysErr = dynamic_cast<const TSystemException*>(&e))
ReportError(errorStr, sysErr->mError, notify);
else
ReportError(errorStr, LoadRavenString(" because ") + e.what() + LoadRavenString(". (period)"), notify);
}
#pragma mark -
// ========================================================================================
// Events
// ========================================================================================
//---------------------------------------------------------------
//
// IsKeyDown
//
//---------------------------------------------------------------
bool IsKeyDown(short keyCode)
{
Byte keyMap[16];
GetKeys((UInt32 *) keyMap);
bool down = ((keyMap[keyCode >> 3] >> (keyCode & 7)) & 1) != 0;
return down;
}
//---------------------------------------------------------------
//
// ExtractChar
//
// Returns the ASCII character corresponding to the keyDown or
// keyUp event. This function can be used to find the character
// that was pressed with the option key or to find out if the
// shift key was held down with the command key. Note that this
// code is adapted from MacApp's TApplication::KeyEventToComponents.
//
//---------------------------------------------------------------
char ExtractChar(const EventRecord& event)
{
ASSERT(event.what == keyDown || event.what == keyUp || event.what == autoKey);
const int kMaskModifier = 0xF600; // strip command and options keys from Modifiers
const long kMaskASCII1 = 0x000000FF; // get key from KeyTranslate return
const long kMaskASCII2 = 0x00FF0000; // get key from KeyTranslate return
const short kUpKeyMask = 0x0080;
// Now see if the command key is down. If it is, get the correct ASCII translation by
// masking the command key out and re-translating because the command key will
// mask the shift modifier.
char theChar = (char) (event.message & charCodeMask);
ushort theKey = (ushort) ((event.message & keyCodeMask) >> 8);
if ((event.modifiers & cmdKey) != 0 || (event.modifiers & optionKey) != 0) {
// Set the upkey bit so KeyTranslate doesn't do special deadkey processing.
// See IM-V pp. 195
ushort keyCodeParameter = (ushort) ((event.modifiers & kMaskModifier) | theKey | kUpKeyMask);
Ptr keyTransTable = (Ptr) (GetScriptManagerVariable(smKCHRCache));
ulong state = 0;
ulong keyInfo = KeyTranslate(keyTransTable, keyCodeParameter, &state);
theChar = (char) (keyInfo & kMaskASCII1);
if (theChar == 0)
theChar = (char) ((keyInfo >> kMaskASCII2) & 16);
}
return theChar;
}
//---------------------------------------------------------------
//
// IsCommandPeriod
//
// Non-US keyboards sometimes use option key to generate period
// so we'll strip off all the modifier keys before checking.
//
//---------------------------------------------------------------
bool IsCommandPeriod(const EventRecord& event)
{
bool cmdPeriod = false;
if (event.what == keyDown)
if ((event.modifiers & cmdKey) != 0 && ExtractChar(event) == kPeriodChar)
cmdPeriod = true;
return cmdPeriod;
}
#pragma mark -
// ===================================================================================
// Misc
// ===================================================================================
//---------------------------------------------------------------
//
// ByteSwap (double)
//
//---------------------------------------------------------------
void ByteSwap(double& inData)
{
ASSERT(sizeof(double) == 2*sizeof(long));
long* data = reinterpret_cast<long*>(&inData);
long temp = data[0];
data[0] = data[1];
data[1] = temp;
byteswaplong(data[0]);
byteswaplong(data[1]);
}
//---------------------------------------------------------------
//
// BuildCRCTable
//
//---------------------------------------------------------------
static unsigned long sCRCTable[256];
static void BuildCRCTable()
{
const ulong kPolynomial = 0xEDB88320L;
for (ulong i = 0; i < 256; i++) {
ulong value = i;
for (int j = 8; j > 0; j--) {
if (value & 1)
value = (value >> 1) ^ kPolynomial;
else
value >>= 1;
}
sCRCTable[i] = value;
}
}
//---------------------------------------------------------------
//
// ComputeCRC
//
// The code uses the CCITT-32 formula which is used by programs
// like PKZIP and ARJ. The algorithm is taken from "The Data
// Compression Book" by Mark Nelson pg 446-447.
//
//---------------------------------------------------------------
ulong ComputeCRC(const void* buffer, ulong bytes, ulong crc)
{
ASSERT(buffer != nil);
ASSERT(bytes < 16*1024L*1024L);
static bool inited = false;
if (!inited) {
BuildCRCTable();
inited = true;
}
// Note that these tests are not part of the original algorithm.
// They've been added because the prefs code sometimes tries to
// do a CRC on just a bool which doesn't work with the original
// algorithm.
if (crc == 0xFFFFFFFFL && bytes == 1)
crc = *((Byte *) buffer);
else if (crc == 0xFFFFFFFFL && bytes == 2)
crc = *((ushort *) buffer);
else if (crc == 0xFFFFFFFFL && bytes == 4)
crc = *((ulong *) buffer);
else {
Byte* p = (Byte *) buffer;
while (bytes--) {
ulong temp1 = (crc >> 8) & 0x00FFFFFFL;
ulong temp2 = sCRCTable[(crc ^ *p++) & 0xFF];
crc = temp1 ^ temp2;
}
}
return crc;
}
//---------------------------------------------------------------
//
// GetMicroSeconds
//
//---------------------------------------------------------------
long long GetMicroSeconds()
{
UnsignedWide microSeconds;
Microseconds(µSeconds);
long long seconds = microSeconds.hi;
seconds <<= 32;
seconds += microSeconds.lo;
return seconds;
}
//---------------------------------------------------------------
//
// GetMilliSeconds
//
//---------------------------------------------------------------
MilliSecond GetMilliSeconds()
{
UnsignedWide microSeconds;
Microseconds(µSeconds);
MilliSecond seconds = (MilliSecond) (microSeconds.hi*4294967L + microSeconds.lo/1000); // 4294967L = 2^32 / 1000
return seconds;
}
//---------------------------------------------------------------
//
// MilliSecondDelay
//
//---------------------------------------------------------------
void MilliSecondDelay(MilliSecond delay)
{
MilliSecond stopTime = GetMilliSeconds() + delay;
while (GetMilliSeconds() < stopTime)
;
}
#pragma mark -
// ========================================================================================
// class TCodeTimer
// ========================================================================================
//---------------------------------------------------------------
//
// TMOverhead
//
// Returns the overhead used by the Time Manager to set up a timer
// (in microseconds).
//
//---------------------------------------------------------------
#if !RELEASE
static long TMOverhead()
{
long startTime = -LONG_MAX;
TMTask task;
QElemPtr taskPtr = (QElemPtr) &task;
task.tmAddr = nil;
task.tmWakeUp = 0;
task.tmReserved = 0;
InsTime(taskPtr);
PrimeTime(taskPtr, startTime);
RmvTime(taskPtr);
return -(startTime - task.tmCount);
}
#endif
//---------------------------------------------------------------
//
// TCodeTimer::~TCodeTimer
//
//---------------------------------------------------------------
#if !RELEASE
TCodeTimer::~TCodeTimer()
{
// Returns the amount of unused time in tmCount (it should all be unused
// since we used a very large delay). The time will be in microseconds
// if the unused time is small or milliseconds if the delay is large.
::RmvTime((QElemPtr) &mTask);
long totalTime, endTime = mTask.tmCount;
if (endTime < 0)
totalTime = mStartTime - endTime;
else
totalTime = mStartTime + (1000L * endTime);
totalTime = labs(totalTime) - TMOverhead();
if (totalTime < 0) // too quick to measure
totalTime = 0;
DEBUGSTR("%s %.3f msecs.", mDescription, totalTime/1000.0);
}
#endif
//---------------------------------------------------------------
//
// TCodeTimer::TCodeTimer
//
//---------------------------------------------------------------
#if !RELEASE
TCodeTimer::TCodeTimer(const char* description)
{
mDescription = description ? description : "Object lived for";
// The Time Manager interprets positive numbers as millisecond values
// and negative numbers as negated microsecond values. Here we install
// a Time Manager task using microseconds that will wake up in 35 minutes.
mStartTime = -LONG_MAX;
mTask.tmAddr = nil;
mTask.tmWakeUp = 0;
mTask.tmReserved = 0;
::InsTime((QElemPtr) &mTask);
::PrimeTime((QElemPtr) &mTask, mStartTime);
}
#endif
//---------------------------------------------------------------
//
// TCodeTimer::OnAbnormalExit
//
// The toolbox does not remove Time Manager tasks when an app shuts
// down so we need to do it to prevent crashes.
//
//---------------------------------------------------------------
#if !RELEASE
void TCodeTimer::OnAbnormalExit()
{
::RmvTime((QElemPtr) &mTask);
}
#endif
#pragma mark -
// ========================================================================================
// class TAverageTimer
// ========================================================================================
//---------------------------------------------------------------
//
// TAverageTimer::~TAverageTimer
//
//---------------------------------------------------------------
#if !RELEASE
TAverageTimer::~TAverageTimer()
{
ASSERT(!mTiming);
double average = mElapsedTime/mNumTimings;
DEBUGSTR("%s %.3f msecs (average), %.3f (max), %.3f (min).", mDescription, average/1000.0, mMaxTime/1000.0, mMinTime/1000.0);
}
#endif
//---------------------------------------------------------------
//
// TAverageTimer::TAverageTimer
//
//---------------------------------------------------------------
#if !RELEASE
TAverageTimer::TAverageTimer(const char* description)
{
mElapsedTime = 0.0;
mMaxTime = 0;
mMinTime = LONG_MAX;
mNumTimings = 0;
mTiming = false;
mDescription = description ? description : "Object lived for";
}
#endif
//---------------------------------------------------------------
//
// TAverageTimer::StartTiming
//
//---------------------------------------------------------------
#if !RELEASE
void TAverageTimer::StartTiming()
{
ASSERT(!mTiming);
// The Time Manager interprets positive numbers as millisecond
// values and negative numbers as negated microsecond values.
// Here we install a Time Manager task using microseconds that
// will wake up in 35 minutes.
mStartTime = -LONG_MAX;
mTask.tmAddr = nil;
mTask.tmWakeUp = 0;
mTask.tmReserved = 0;
::InsTime((QElemPtr) &mTask);
::PrimeTime((QElemPtr) &mTask, mStartTime);
mTiming = true;
}
#endif
//---------------------------------------------------------------
//
// TAverageTimer::StopTiming
//
//---------------------------------------------------------------
#if !RELEASE
void TAverageTimer::StopTiming()
{
ASSERT(mTiming);
// Returns the amount of unused time in tmCount (it should all
// be unused since we used a very large delay). The time will
// be in microseconds if the unused time is small or milliseconds
// if the delay is large.
::RmvTime((QElemPtr) &mTask);
long elapsed, endTime = mTask.tmCount;
if (endTime < 0)
elapsed = mStartTime - endTime;
else
elapsed = mStartTime + 1000L*endTime;
elapsed = labs(elapsed) - TMOverhead();
if (elapsed < 0) // too quick to measure
elapsed = 0;
if (elapsed < mMinTime)
mMinTime = elapsed;
if (elapsed > mMaxTime)
mMaxTime = elapsed;
mElapsedTime += elapsed;
mNumTimings++;
mTiming = false;
}
#endif
//---------------------------------------------------------------
//
// TAverageTimer::OnAbnormalExit
//
// The toolbox does not remove Time Manager tasks when an app shuts
// down so we need to do it to prevent crashes.
//
//---------------------------------------------------------------
#if !RELEASE
void TAverageTimer::OnAbnormalExit()
{
::RmvTime((QElemPtr) &mTask);
}
#endif